package com.idealighter.game.games.blackjack.struct;

import com.google.common.collect.Lists;
import com.google.common.primitives.Ints;

import com.idealighter.game.core.assertions.HuohuaAssert;
import com.idealighter.game.core.exception.HuohuaRunTimeException;
import com.idealighter.game.games.common.AbstractTable;
import com.idealighter.utils.check.EmptyUtil;
import com.idealighter.utils.code.RandCodeUtil;
import com.idealighter.utils.json.JsonUtil;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ScheduledFuture;

import lombok.Data;
import lombok.EqualsAndHashCode;

/**
 * 百家乐桌子 .
 * 
 * @date 2016年2月17日 上午10:46:55
 */
@Data
@EqualsAndHashCode(callSuper = true)
public class BlackjackTable extends AbstractTable {

  public ScheduledFuture<?> stepFuture = null;

  // 座位数量
  private static final int SEAT_NUM = 5;
  // 几副牌
  private static final int CARD_NUM = 8;
  // 最大拿几张牌
  private static final int MAX_CARD_NUM = 5;

  private final BlackjackRoom room;

  // 房间里面的桌子
  private final List<BlackjackSeat> seats;

  // 庄家牌
  private List<BlackjackCard> bankerCards = Lists.newArrayList();

  // 当前牌型.
  private BlackjackCardsTypeAndPoint bankerTypeAndPoint;

  // 一副牌
  private final List<BlackjackCard> cards;

  private final int[] cardNums = new int[14];

  // 使用的卡牌，收集起来，以便牌不够的时候还能接着补牌
  private final List<BlackjackCard> usedCards;
  // 一副牌 - 不变的
  private final List<BlackjackCard> finalcards;
  // 当前状态
  private GameStatus status = GameStatus.REST;

  // 游戏步骤(休息、下注、开奖、结算)定时Future


  // 当前激活座位号
  private int activeSeatOrder = 0;
  // 当前激活牌号
  private int activeCardOrder = 0;
  // 游戏序列号
  private int serial = 0;

  // 游戏进行中
  private boolean gameing;

  // 系统输赢
  private long sysWin = 0;

  /**
   * 桌子构造函数.
   * 
   * @param id .
   * @param room .
   */
  public BlackjackTable(int id, BlackjackRoom room) {
    super(id);

    this.room = room;
    this.seats = createSeats();
    this.finalcards = createCards();
    this.cards = Lists.newArrayList();
    cards.addAll(finalcards);
    this.usedCards = Lists.newArrayList();
    Arrays.fill(this.cardNums, 8);
  }

  private List<BlackjackCard> createCards() {
    List<BlackjackCard> cards = Lists.newArrayList();
    for (int i = 0; i < CARD_NUM; i++) {
      cards.addAll(Arrays.asList(BlackjackCard.values()));
    }
    // 随机打乱
    Collections.shuffle(cards);
    return cards;
  }

  /**
   * 创建位置 .
   * 
   * @return *
   */
  private List<BlackjackSeat> createSeats() {
    List<BlackjackSeat> seats = new ArrayList<>(SEAT_NUM);
    for (int i = 0; i < SEAT_NUM; i++) {
      seats.add(new BlackjackSeat(i, this));
    }

    return Collections.unmodifiableList(seats);

  }

  /**
   * 是否洗牌 .
   * 
   * 
   * @return
   */
  private boolean isWashCard() {
    return cards.size() < seats.size() * (MAX_CARD_NUM + 1);
  }

  /**
   * 洗牌 .
   *
   * @Title washCard .
   * @author abin
   * @date 2018年3月21日 下午3:55:57
   */
  private void washCard() {
    cards.clear();
    cards.addAll(finalcards);
    Collections.shuffle(cards);
    usedCards.clear();
  }

  /**
   * 删除已发的牌 .
   * 
   * 
   * @param blackjackCards . 已发的牌
   */
  public void removeCard(List<BlackjackCard> blackjackCards) {
    for (BlackjackCard btCard : blackjackCards) {
      cards.remove(btCard);
    }
  }

  /**
   * 是否存在该牌 .
   * 
   * 
   * @param blackjackCard .
   * @return
   */
  public boolean isCardExist(BlackjackCard blackjackCard) {
    for (BlackjackCard btCard : cards) {
      if (BlackjackCard.equal(btCard, blackjackCard)) {
        return true;
      }
    }
    return false;

  }



  /**
   * 重置桌子游戏数据 .
   */
  public void resetGameData() {
    // 需要洗牌，则洗牌
    if (isWashCard()) {
      washCard();
    }
    this.serial = 0;
    this.status = GameStatus.REST;
    this.bankerCards.clear();
    this.bankerTypeAndPoint = null;
    this.activeCardOrder = 0;
    this.activeSeatOrder = 0;

    for (BlackjackSeat seat : this.seats) {
      seat.resetGameData();
    }
  }

  /**
   * 获取seat .
   * 
   * @param order .
   * @return
   * 
   */
  public BlackjackSeat getSeat(int order) {

    return seats.get(order);
  }

  /**
   * 房间是否有玩家 .
   *
   * @author abin
   * @date 2018年3月23日 下午1:41:28
   * @return
   */
  public boolean hasPlayer() {
    boolean result = false;
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && seat.seated()) {
        result = true;
        break;
      }
    }
    return result;
  }

  /**
   * 是否全部下注完成 .
   *
   * @author abin
   * @date 2018年3月23日 下午4:56:15
   * @return
   */
  public boolean isAllBetOver() {
    boolean result = true;
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && !seat.isBetOver()) {
        result = false;
        break;
      }
    }
    return result;
  }

  /**
   * 是否有玩家下注.
   *
   * @author abin
   * @date 2018年3月28日 下午2:16:00
   * @return 结果.
   */
  public boolean hasPlayerBet() {
    boolean result = false;
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && seat.isHasBet()) {
        result = true;
        break;
      }
    }
    return result;
  }



  /**
   * 参与游戏的是否都 操作保险了 .
   *
   * @author abin
   * @date 2018年3月24日 下午3:57:54
   * @return 结果.
   */
  public boolean isAllInsuranceOver() {
    boolean result = true;
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && seat.isHasBet() && !seat.isInsuranceOver()) {
        result = false;
        break;
      }
    }
    return result;
  }

  /**
   * 是否全部已经结算 .
   *
   * @author abin
   * @date 2018年3月28日 下午3:56:21
   * @return 结果.
   */
  public boolean isAllBilled() {
    boolean result = true;
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && seat.isHasBet()) {
        for (BlackjackCardSeat cardSeat : seat.getCardSeats()) {
          if (!cardSeat.isBill()) {
            result = false;
            break;
          }
        }
        if (!result) {
          break;
        }
      }
    }
    return result;
  }

  @SuppressWarnings("unchecked")
  @Override
  public BlackjackRoom room() {
    return room;
  }

  @Override
  public List<BlackjackSeat> seats() {

    return seats;
  }

  private BlackjackCard popCard() {
    BlackjackCard card = null;
    if (cards.size() > 0) {
      int index = cards.size() - 1;
      card = cards.remove(index);
      usedCards.add(card);
      this.cardNums[card.getPower()] = this.cardNums[card.getPower()] - 1;
    } else { // 防止分牌太多，牌不够
      int index = RandCodeUtil.random(usedCards.size());
      card = usedCards.remove(index);
    }
    return card;
  }

  /**
   * 根据点数，输入卡牌 .
   *
   * @author abin
   * @date 2018年3月31日 下午3:33:47
   * @param power 点数.
   * @return 结果.
   * @throws HuohuaRunTimeException 不可能找不到，报异常.
   */
  private BlackjackCard popCardByPower(Integer power) {
    BlackjackCard result = null;
    for (int i = 0; i < this.cards.size(); i++) {
      if (this.cards.get(i).getPower() == power) {
        result = this.cards.remove(i);
        break;
      }
    }
    HuohuaAssert.notNull(result);
    return result;
  }

  /**
   * 归还一张牌 .
   *
   * @author abin
   * @date 2018年3月31日 下午3:20:33
   * @param card 牌组.
   */
  private void pushCard(BlackjackCard card) {
    cards.add(card);
    usedCards.remove(card);
    this.cardNums[card.getPower()] = this.cardNums[card.getPower()] + 1;
  }

  /**
   * 发牌阶段发牌.
   *
   * @author abin
   * @date 2018年3月23日 下午7:06:33
   */
  public void dealCard() {

    // 分庄家牌
    // for test
    // this.bankerCards.add(popCardByPower(1));
    // this.bankerCards.add(popCard());

    this.bankerCards.add(popCard());
    this.bankerCards.add(popCard());
    this.bankerTypeAndPoint = BlackjackCardsTypeGetter.getCardsType(bankerCards, false);

    // 分个人牌
    for (BlackjackSeat seat : seats) {
      if (seat.getPlayerId() > 0 && seat.isHasBet()
          && EmptyUtil.listIsNotEmpty(seat.getCardSeats())) {
        BlackjackCardSeat cardSeat = seat.getDefaultCardSeat();
        List<BlackjackCard> playerCards = cardSeat.getPlayerCards();

        // for test

        // BlackjackCard blackjackCard = popCard();
        //
        // playerCards.add(blackjackCard);
        // playerCards.add(popCardByPower(blackjackCard.power));

        // normal
        playerCards.add(popCard());
        playerCards.add(popCard());
        BlackjackCardsTypeAndPoint cardsAndPoint =
            BlackjackCardsTypeGetter.getCardsType(playerCards, seat.isSplit());
        cardSeat.setCardTypeAndPoint(cardsAndPoint);
      }
    }
  }

  /**
   * 单人补牌.
   *
   * @author abin
   * @date 2018年3月27日 上午11:37:51
   * @param seat 座位信息.
   * @param cardSeat 牌组信息.
   */
  public void dealSingleCard(BlackjackSeat seat, BlackjackCardSeat cardSeat) {
    List<BlackjackCard> playerCards = cardSeat.getPlayerCards();
    playerCards.add(popCard());
    BlackjackCardsTypeAndPoint cardsAndPoint =
        BlackjackCardsTypeGetter.getCardsType(playerCards, seat.isSplit());
    cardSeat.setCardTypeAndPoint(cardsAndPoint);
  }


  /**
   * 分牌.
   *
   * @author abin
   * @date 2018年3月27日 下午3:54:11
   * @param cardSeat 分牌的牌.
   * @param newCardSeat 新牌.
   */
  public void dealSplitCard(BlackjackCardSeat cardSeat, BlackjackCardSeat newCardSeat) {
    BlackjackCard splitCard = cardSeat.getPlayerCards().remove(1);

    newCardSeat.getPlayerCards().add(splitCard);

    // for test
    // cardSeat.getPlayerCards().add(popCardByPower(splitCard.power));

    // normal
    cardSeat.getPlayerCards().add(popCard());

    BlackjackCardsTypeAndPoint cardsAndPoint =
        BlackjackCardsTypeGetter.getCardsType(cardSeat.getPlayerCards(), true);
    cardSeat.setCardTypeAndPoint(cardsAndPoint);

    // for test
    // newCardSeat.getPlayerCards().add(popCardByPower(splitCard.power));

    // normal
    newCardSeat.getPlayerCards().add(popCard());

    BlackjackCardsTypeAndPoint newCardsAndPoint =
        BlackjackCardsTypeGetter.getCardsType(newCardSeat.getPlayerCards(), true);
    newCardSeat.setCardTypeAndPoint(newCardsAndPoint);
  }

  /**
   * 补牌.
   *
   * @author abin
   * @date 2018年3月28日 上午9:56:12
   * @param cardSeat 牌组位置.
   * @param split 是否分牌.
   */
  public void dealHitCard(BlackjackCardSeat cardSeat, boolean split) {
    cardSeat.getPlayerCards().add(popCard());
    BlackjackCardsTypeAndPoint cardsAndPoint =
        BlackjackCardsTypeGetter.getCardsType(cardSeat.getPlayerCards(), split);
    cardSeat.setCardTypeAndPoint(cardsAndPoint);
  }



  /**
   * 随机牌型与点数 .
   *
   * @author abin
   * @date 2018年3月31日 下午1:41:28
   * @param suitCardTypes 合适的牌型.
   * @return 随机结果牌型与点数.
   */
  private BlackjackCardsTypeAndPoint randomCardsTypeAndPoint(
      List<BlackjackCardsTypeAndPoint> suitCardTypes) {
    int totalWeight = BlackjackCardsTypeWeight.totalWeight(suitCardTypes);

    System.out.println(totalWeight);
    System.out.println(JsonUtil.toJson(suitCardTypes));

    // 随机数
    int rand = RandCodeUtil.random(totalWeight);
    int sum = 0;
    BlackjackCardsTypeAndPoint randomCardsTypeAndPoint = null;

    for (int index = 0; index < suitCardTypes.size(); index++) {
      sum += BlackjackCardsTypeWeight.getWeight(suitCardTypes.get(index));
      if (rand < sum) {
        randomCardsTypeAndPoint = suitCardTypes.remove(index);
        break;
      }
    }
    return randomCardsTypeAndPoint;
  }


  private List<Integer> randomIndexs() {
    List<Integer> list = Ints.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13);
    Collections.shuffle(list);
    return list;
  }

  private List<Integer> findCardByCardsTypeAndPoint(
      BlackjackCardsTypeAndPoint randomCardsTypeAndPoint) {

    List<Integer> twoIndexs = randomIndexs();
    List<Integer> threeIndexs = randomIndexs();
    List<Integer> fourIndexs = randomIndexs();
    List<Integer> fiveIndexs = randomIndexs();

    List<Integer> cards = new ArrayList<>();
    // 移除庄家第二张 牌
    BlackjackCard removeCard = this.bankerCards.remove(1);
    this.cardNums[removeCard.getPower()] = this.cardNums[removeCard.getPower()] + 1;

    cards.add(this.bankerCards.get(0).getNum());

    boolean isOver = false;
    for (int i = 0; i < twoIndexs.size() && !isOver; i++) {


      // 处理第二张牌.
      int two = twoIndexs.get(i);
      if (this.cardNums[two] == 0) {

        continue;
      }
      cards.add(two);
      this.cardNums[two] = this.cardNums[two] - 1;


      BlackjackCardsTypeAndPoint tempCardsType =
          BlackjackCardsTypeGetter.getCardsTypeByPower(cards);
      if (tempCardsType.equals(randomCardsTypeAndPoint)) {
        isOver = true;
      }


      if (!isOver && !BlackjackCardsTypeGetter.bankCanHit(tempCardsType)) {
        cards.remove(1);
        this.cardNums[two] = this.cardNums[two] + 1;
        continue;
      }

      for (int j = 0; j < threeIndexs.size() && !isOver; j++) {

        // 处理第三张牌
        int three = threeIndexs.get(j);

        if (this.cardNums[three] == 0) {
          continue;
        }

        cards.add(three);
        this.cardNums[three] = this.cardNums[three] - 1;

        tempCardsType = BlackjackCardsTypeGetter.getCardsTypeByPower(cards);
        if (tempCardsType.equals(randomCardsTypeAndPoint)) {
          isOver = true;
        }

        if (!isOver && !BlackjackCardsTypeGetter.bankCanHit(tempCardsType)) {
          cards.remove(2);
          this.cardNums[three] = this.cardNums[three] + 1;
          continue;
        }

        for (int z = 0; z < fourIndexs.size() && !isOver; z++) {

          // 处理第四张牌
          int four = fourIndexs.get(z);

          if (this.cardNums[four] == 0) {
            continue;
          }

          cards.add(four);
          this.cardNums[four] = this.cardNums[four] - 1;

          tempCardsType = BlackjackCardsTypeGetter.getCardsTypeByPower(cards);
          if (tempCardsType.equals(randomCardsTypeAndPoint)) {
            isOver = true;
          }

          if (!isOver && !BlackjackCardsTypeGetter.bankCanHit(tempCardsType)) {
            cards.remove(3);
            this.cardNums[four] = this.cardNums[four] + 1;
            continue;
          }

          for (int k = 0; k < fiveIndexs.size() && !isOver; k++) {
            // 处理第五张牌
            int five = fiveIndexs.get(k);

            if (this.cardNums[five] == 0) {
              continue;
            }

            cards.add(five);
            this.cardNums[five] = this.cardNums[five] - 1;

            tempCardsType = BlackjackCardsTypeGetter.getCardsTypeByPower(cards);
            if (tempCardsType.equals(randomCardsTypeAndPoint)) {
              isOver = true;
            }


            if (!isOver) { // 第五张牌不满足，回退．
              cards.remove(4);
              this.cardNums[five] = this.cardNums[five] + 1;
            }

          }


          if (!isOver) { // 第四张牌不满足，回退．
            cards.remove(3);
            this.cardNums[four] = this.cardNums[four] + 1;
          }

        }


        if (!isOver) { // 第三张牌不满足，回退．
          cards.remove(2);
          this.cardNums[three] = this.cardNums[three] + 1;
        }
      }


      if (!isOver) { // 第二张牌不满足，回退．
        cards.remove(1);
        this.cardNums[two] = this.cardNums[two] + 1;
      }
    }

    if (!isOver) {
      this.bankerCards.add(removeCard);
      pushCard(removeCard);
      cards = null;
    } else {
      cards.remove(0);
    }
    return cards;
  }

  /**
   * 庄家补牌 .
   *
   * @author abin
   * @date 2018年3月28日 下午1:45:01
   */
  public void dealBankerCard() {
    while (BlackjackCardsTypeGetter.bankCanHit(this.bankerTypeAndPoint)) {
      this.bankerCards.add(popCard());
      this.bankerTypeAndPoint = BlackjackCardsTypeGetter.getCardsType(this.bankerCards, false);
    }
  }

  /**
   * 处理特殊牌型 补牌 .
   *
   * @author abin
   * @date 2018年3月31日 下午3:06:13
   * @param suitCardTypes 可以开出牌型.
   */
  public void dealBankerCard(List<BlackjackCardsTypeAndPoint> suitCardTypes) {

    // 查找牌
    BlackjackCardsTypeAndPoint cardsTypeAndPoint = null;
    List<Integer> cards = null;

    if (EmptyUtil.listIsNotEmpty(suitCardTypes)) {
      while ((cardsTypeAndPoint = randomCardsTypeAndPoint(suitCardTypes)) != null) {
        cards = findCardByCardsTypeAndPoint(cardsTypeAndPoint);
        if (cards != null) {
          break;
        }
      }
    }

    if (cards == null) {
      this.dealBankerCard();
    } else {
      for (Integer power : cards) {
        this.bankerCards.add(popCardByPower(power));
      }
      this.bankerTypeAndPoint = BlackjackCardsTypeGetter.getCardsType(bankerCards, false);
    }
  }



  /**
   * 庄家第一张牌是不是 .
   *
   * @author abin
   * @date 2018年3月24日 下午3:23:17
   * @return 结果.
   */
  public boolean isFirstA() {
    boolean result = false;
    if (EmptyUtil.listIsNotEmpty(this.bankerCards)) {
      result = this.bankerCards.get(0).getPower() == BlackjackCard.HEI_TAO_A.getPower();
    }
    return result;
  }

  /**
   * 补牌.
   *
   * @author abin
   * @date 2018年3月23日 下午7:19:27
   * @param seat 座位信息.
   */
  public void playerHitCard(BlackjackSeat seat) {
    BlackjackCardSeat cardSeat = seat.getDefaultCardSeat();
    List<BlackjackCard> playerCards = cardSeat.getPlayerCards();
    playerCards.add(popCard());
    BlackjackCardsTypeAndPoint cardsAndPoint =
        BlackjackCardsTypeGetter.getCardsType(playerCards, seat.isSplit());
    cardSeat.setCardTypeAndPoint(cardsAndPoint);
  }

}
